import os
import sys
import json
import struct
import socket


class Server(socket.socket):

    def __init__(self, ip, port):
        super().__init__()
        self.ip = ip
        self.port = port

    def initilize(self):
        """初始化过程，绑定端口"""

        print(f'服务端启动中 {(self.ip, self.port)}')
        self.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.bind((self.ip, self.port))
        self.listen()
        print('服务端启动完成，等待客户端连接')

    def start(self):
        self._run_server()

    def _run_server(self):
        """死循环等待客户端连接通信"""
        while True:
            conn, addr = self.accept()
            print(addr, "已连接！")
            ServerLoop(conn, addr).loop_forever()


class ServerLoop():

    def __init__(self, conn, addr):
        self.path = "/"
        self.order_map = {
            "ls": "show_dir",
            "cd": "cd_dir",
            "download": "download_file",
            "pwd": "get_pwd",
        }
        self.conn = conn
        self.addr = addr

    def loop_forever(self):
        try:
            while True:
                print(f"服务器等待来自{self.addr}的请求！")
                header = self.conn.recv(4)
                header_dict = self._parser_header(header)
                print(f"服务器收到来自{self.addr}的消息，内容：", header_dict)

                order, order_content = self.parser_order(header_dict)

                self.execute_order(order, order_content)

        except Exception as e:
            sys.stderr.write(str(e) + "\n")

            print(self.addr, "已终止本次连接！")
            self.conn.close()

    def execute_order(self, order, order_content):
        """处理客户端命令"""
        try:
            if order in self.order_map:
                func_str = self.order_map[order]
            else:
                raise Exception("未知命令！")
            if hasattr(self, func_str):
                func = getattr(self, func_str)
                _args = func.__code__.co_argcount - 1
                if _args:
                    args = order_content[0: _args]
                    func(*args)
                else:
                    func()
            else:
                raise ValueError("未知命令！")
        except Exception as e:
            sys.stderr.write(str(e))

            self._send_msg(Response.OrderError(path=self.path))

    def parser_order(self, back_msg):
        """解析客户端包信息"""

        order = back_msg.get("order", None)
        order_content = back_msg.get("order_content", [])

        return order, order_content

    def _parser_header(self, header):
        """解析客户端包头"""
        header_dict = json.loads(self.conn.recv(struct.unpack("i", header)[0]).decode("utf8"))
        return header_dict

    def _build_header(self, send_dict):
        """封装回包包头"""
        bytes_msg = json.dumps(send_dict).encode("utf8")
        header = struct.pack("i", len(bytes_msg))
        return header, bytes_msg

    def _send_msg(self, send_dic, path=None):
        """发送消息"""

        header, bytes_msg = self._build_header(send_dic)
        self.conn.send(header)  # 发包头
        self.conn.send(bytes_msg)  # 发消息体
        if path:
            with open(path, "rb") as f:
                for line in f:
                    self.conn.send(line)
        print("执行完毕：", send_dic)

    def get_pwd(self):
        """pwd 命令  获取服务端当前所处目录"""
        data = {"data": self.path}
        self._send_msg(Response.Success(data, path=self.path))

    def show_dir(self):
        """ls 命令 获取服务端当前所处目录文件列表"""

        dir_list = os.listdir(self.path)
        new_list = []
        for name in dir_list:
            if os.path.isdir(os.path.join(self.path, name)):
                new_list.append(f"{name}(目录)")
            else:
                new_list.append(f"{name}(文件)")
        data = {"data": new_list}
        send_msg = Response.Success(data=data, path=self.path)

        self._send_msg(send_msg)

    def cd_dir(self, dir_name):
        """cd 命令 进入某个目录"""
        if dir_name == "..":
            path = os.path.dirname(os.path.dirname(self.path))
            self.path = path
            data = {"data": self.path}
            send_msg = Response.Success(data=data, path=self.path)
        else:
            path = os.path.join(self.path, dir_name)
            if os.path.exists(path):
                self.path = path
                data = {"data": self.path}
                send_msg = Response.Success(data=data, path=self.path)
            else:
                send_msg = Response.DirNotExist(path=self.path)

        self._send_msg(send_msg)

    def download_file(self, file_name):
        """download 命令 下载文件"""
        path = os.path.join(self.path, file_name)
        if not os.path.exists(path) or os.path.isdir(path):
            path = None
            send_msg = Response.FileNotExist(path=self.path)
        else:
            size = os.path.getsize(path)
            data = {"data": {"size": size, "file_name": file_name}}
            send_msg = Response.Success(data=data, path=self.path)

        self._send_msg(send_msg, path=path)


class Response(dict):
    """封装回包消息格式"""

    def __init__(self, result=True, msg="", data=None, path=None):
        super().__init__()
        self.__setitem__("result", result)
        self.__setitem__("data", data)
        self.__setitem__("msg", msg)
        self.__setitem__("path", path)

    @classmethod
    def Success(cls, data={}, path=None):
        return Response(msg="成功", data=data, path=path)

    @classmethod
    def FileNotExist(cls, data={}, path=None):
        return Response(result=False, msg="文件不存在！", data=data, path=path)

    @classmethod
    def DirNotExist(cls, data={}, path=None):
        return Response(result=False, msg="目录不存在！", data=data, path=path)

    @classmethod
    def OrderError(cls, data={}, path=None):
        return Response(result=False, msg="命令有误！", data=data, path=path)
